package com.iwhalecloud.citybrain.flow.platform.manager.application.service.impl;

import java.util.*;

import com.alibaba.fastjson.JSONObject;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.command.CreateProcessInstanceCmd;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.event.CreateParticipantEvent;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.threadlocal.CommonOperateLogHolder;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.SpringUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ConstanceVariable;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ErrorCode;
import com.iwhalecloud.citybrain.flow.platform.manager.common.client.HttpCommandDto;
import com.iwhalecloud.citybrain.flow.platform.manager.common.client.HttpCommandResultDto;
import com.iwhalecloud.citybrain.flow.platform.manager.common.client.HttpCreateProcessInstanceDto;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.enums.CommentTypeEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.FlowProcessInstanceTraceVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.ModelFlowObjectProcessInstance;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.ModelFlowObjectProcessInstanceDomain;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowObjectProcessInstanceRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowProcessInstanceTraceRepository;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.engine.ManagementService;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * Handler - 创建流程实例
 *
 * @author lilh
 * @date 2019-02-19 16:13
 */
@Service
public class CreateProcessInstanceCommandHandler extends AbstractCommandHandler {

    @Autowired
    private ManagementService managementService;

    @Autowired
    private FlowProcessInstanceTraceRepository flowProcessInstanceTraceRepository;


    @Autowired
    private FlowObjectProcessInstanceRepository flowObjectProcessInstanceRepository;

    @Override
    public String handleType() {
        return CREATE_PROCESS_INSTANCE;
    }

    @Override
    public HttpCommandResultDto handle(String params) {
        HttpCreateProcessInstanceDto createProcessInstanceDto = JSONObject.parseObject(params, HttpCreateProcessInstanceDto.class);
        return handle(createProcessInstanceDto);
    }

    @Override
    public HttpCommandResultDto handle(HttpCommandDto commandDto) {
        HttpCreateProcessInstanceDto createProcessInstanceDto = (HttpCreateProcessInstanceDto)commandDto;
        CommonOperateLogHolder.getCommonOperateLog().setModelKey(createProcessInstanceDto.getFlowPackageCode());
        CommonOperateLogHolder.getContent().setRequest(createProcessInstanceDto);

        if (StringUtils.isBlank(createProcessInstanceDto.getParticipantId())){
            return resolveResult("流程发起人[participantId]不能为空",null);
        }

        ProcessInstance processInstance = managementService.executeCommand(new CreateProcessInstanceCmd(createProcessInstanceDto));
        //1.发送创建流程实例事件，完成一些额外的操作
        publishEvent(createProcessInstanceDto, processInstance);
        updateProcessInstanceTrace(processInstance, createProcessInstanceDto);

        //2.保存对象实例和流程实例的关系，如果需要的话
        processFlowObjectProcessInstanceDataInit(processInstance, createProcessInstanceDto);

        //3.保存流程发起意见
        Map<String,Object> flowParam = createProcessInstanceDto.getFlowParamList();
        if (MapUtils.isNotEmpty(flowParam)) {
            Object commentObj = flowParam.get(ConstanceVariable.COMMENT);
            if (Objects.nonNull(commentObj)) {
                String comment = (String) commentObj;
                addComment(null, processInstance.getStartUserId(), processInstance.getProcessInstanceId(), CommentTypeEnum.TJ.getName(), comment);
            }
        }

        //4.构建返回结果httpCommandResultDto对象
        return resolveResult(processInstance, ErrorCode.CREATE_PROCESS_INSTANCE_FAILED.errMessage(), CREATE_PROCESS_INSTANCE_SUCCESS);
    }

    private void updateProcessInstanceTrace(ProcessInstance processInstance, HttpCreateProcessInstanceDto createProcessInstanceDto) {
        if (Objects.isNull(processInstance)) {
            return;
        }

        FlowProcessInstanceTraceVo trace = flowProcessInstanceTraceRepository.getProcessInstanceTraceByProcessInstanceId(processInstance.getProcessInstanceId());
        if (Objects.isNull(trace)) {
            return;
        }
        trace.setStarterId(createProcessInstanceDto.getParticipantId());
        trace.setStarterName(createProcessInstanceDto.getParticipantName());
        flowProcessInstanceTraceRepository.update(Collections.singletonList(trace));
    }


    private void publishEvent(HttpCreateProcessInstanceDto createProcessInstanceDto, ProcessInstance processInstance) {
        if (Objects.isNull(processInstance)) {
            logger.warn("创建流程实例失败，不发布事件");
            return;
        }
        createProcessInstanceDto.setProcessInstanceId(processInstance.getProcessInstanceId());
        CreateParticipantEvent.CreateParticipantVo createParticipantVo = new CreateParticipantEvent.CreateParticipantVo();
        BeanUtils.copyProperties(createProcessInstanceDto, createParticipantVo);
        SpringUtils.publishEvent(new CreateParticipantEvent(createParticipantVo, processInstance));
    }

    /**
     * 流程实例发布关联objectProcessInstanceID对象
     * @param processInstance
     * @param createProcessInstanceDto
     */
    private void processFlowObjectProcessInstanceDataInit(ProcessInstance processInstance, HttpCreateProcessInstanceDto createProcessInstanceDto) {
        if (Objects.isNull(processInstance)) {
            return;
        }
        String objectInstanceId = createProcessInstanceDto.getObjectInstanceId();
        String processInstanceId = processInstance.getProcessInstanceId();
        // 如果objectInstanceId不为空，则关联当前流程实例ID进行入库保存操作
        if (StringUtils.isNotBlank(objectInstanceId) && StringUtils.isNotBlank(processInstanceId)) {
            List<ModelFlowObjectProcessInstanceDomain> objectProcessInstanceList = flowObjectProcessInstanceRepository.getFlowObjectProcessInstanceDomailList(null, processInstanceId, null);
            if (objectProcessInstanceList!=null && objectProcessInstanceList.size()>0) {
                logger.error("=====发布流程生产的流程实例ID在对象实例关联表中已存在！");
            } else {
                ModelFlowObjectProcessInstance record = new ModelFlowObjectProcessInstance();
                record.setCreateTime(new Date());
                record.setProcessInstanceId(processInstanceId);
                record.setObjectInstanceId(objectInstanceId);
                flowObjectProcessInstanceRepository.insertSelective(record);
            }
        }
    }
}
